//GLobal
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

//Types
import {
  IUser,
  IUserState,
  IPizzaCartItem,
  IUpdatePizza,
  INewObj,
} from "@/types/types";
import { requestToAPI } from "@/services";

//Utils
import { arraysAreEqual } from "@/utils/arrayСomparison";

const initialState: IUserState = {
  users: [],
  currentUser: {
    name: null,
    surname: null,
    phoneNumber: null,
    email: null,
    id: null,
    token: null,
    userCart: [],
  },
  status: "pending",
};

export const fetchCart = createAsyncThunk<
  IUser["userCart"],
  undefined,
  { rejectValue: string }
>("userSlice/fetchCart", async (_, { rejectWithValue }) => {
  try {
    const { userCart } = await requestToAPI<IUser>("/currentUser", "get");

    return userCart;
  } catch (e) {
    return rejectWithValue("Can't fetch your cart. Server error.");
  }
});

export const addToCart = createAsyncThunk<
  IPizzaCartItem,
  IPizzaCartItem,
  { rejectValue: string; state: { user: typeof initialState } }
>("userSlice/addToCart", async (newPizza, { rejectWithValue, getState }) => {
  try {
    const { userCart } = getState().user.currentUser,
      newObj: INewObj = {
        userCart: [...userCart, newPizza],
      };

    await requestToAPI<IUser>("/currentUser", "patch", newObj);

    return newPizza;
  } catch (e) {
    return rejectWithValue("Can't add pizza to cart. Server error.");
  }
});

export const deletePizzaFromCart = createAsyncThunk<
  string,
  string,
  { rejectValue: string; state: { user: typeof initialState } }
>(
  "userSlice/deletePizzaFromCart",
  async (id, { rejectWithValue, getState }) => {
    try {
      const { userCart } = getState().user.currentUser;

      const newObj = {
        userCart: userCart.filter(item => item.id !== id),
      };

      await requestToAPI<IUser>(`/currentUser`, "patch", newObj);

      return id;
    } catch (e) {
      return rejectWithValue("Something went wrong! Server Error.");
    }
  }
);

export const updatePizzaDetails = createAsyncThunk<
  IPizzaCartItem,
  IUpdatePizza,
  { rejectValue: string; state: { user: typeof initialState } }
>(
  "userSlice/updatePizzaDetails",
  async ({ actionCounter, pizza }, { rejectWithValue, getState }) => {
    try {
      const { userCart } = getState().user.currentUser;

      const updatedUserCart = userCart.map(item => {
        if (item.id === pizza.id) {
          const updatedItem = { ...item };

          if (actionCounter) {
            updatedItem.count =
              actionCounter === "inc"
                ? updatedItem.count + 1
                : updatedItem.count - 1;
          }

          updatedItem.totalPrice = updatedItem.count * updatedItem.pizzaPrice;

          return updatedItem;
        }

        return item;
      });

      await requestToAPI(`/currentUser`, "patch", {
        userCart: updatedUserCart,
      });

      return (
        updatedUserCart.find(item => item.id === pizza.id) ||
        rejectWithValue("Failed to update pizza details.")
      );
    } catch (e) {
      return rejectWithValue("Something went wrong! Server Error.");
    }
  }
);

export const clearUserCart = createAsyncThunk<
  undefined,
  undefined,
  { rejectValue: string }
>("userSlice/clearUserCart", async (_, { rejectWithValue }) => {
  try {
    const newObj: INewObj = {
      userCart: [],
    };

    await requestToAPI<IUser["userCart"]>("/currentUser", "patch", newObj);
  } catch (e) {
    return rejectWithValue("Something went wrong!");
  }
});

export const setUsers = createAsyncThunk<
  IUserState["users"],
  undefined,
  { rejectValue: string; state: { user: IUserState } }
>("userSlice/setUsers", async (_, { rejectWithValue, getState }) => {
  try {
    const { userCart, id } = getState().user.currentUser;
    const userFromArray = getState().user.users.find(
      userItem => userItem.id === id
    );

    if (userFromArray) {
      const areEqual = arraysAreEqual(userCart, userFromArray?.userCart);

      if (!areEqual) {
        await requestToAPI<IUser>(`/users/${id}`, "patch", {
          userCart: userCart,
        });
      }
    }

    return await requestToAPI<IUserState["users"]>("/users", "get");
  } catch (e) {
    return rejectWithValue("Something went wrong!");
  }
});

export const addUser = createAsyncThunk<IUser, IUser, { rejectValue: string }>(
  "userSlice/addUser",
  async (newUser, { rejectWithValue }) => {
    try {
      await requestToAPI<IUser>("/users", "post", newUser);

      return newUser;
    } catch (e) {
      return rejectWithValue("Something went wrong!");
    }
  }
);

export const setUser = createAsyncThunk<IUser, IUser, { rejectValue: string }>(
  "userSlice/setUser",
  async (newUser, { rejectWithValue }) => {
    try {
      await requestToAPI<IUserState>("/currentUser", "put", newUser);

      return newUser;
    } catch (e) {
      return rejectWithValue("Something went wrong!");
    }
  }
);

export const resetUser = createAsyncThunk<
  IUserState,
  undefined,
  { rejectValue: string }
>("userSlice/resetUser", async (_, { rejectWithValue }) => {
  try {
    const newObj: IUserState["currentUser"] = {
      name: null,
      surname: null,
      phoneNumber: null,
      email: null,
      id: null,
      token: null,
      userCart: [],
    };

    const response = await requestToAPI<IUserState>(
      "currentUser",
      "put",
      newObj
    );

    return await response;
  } catch (e) {
    return rejectWithValue("Something went wrong!");
  }
});

const userSlice = createSlice({
  name: "userSlice",
  initialState,
  reducers: {},
  extraReducers: builder => {
    builder
      .addCase(fetchCart.fulfilled, (state, action) => {
        state.currentUser.userCart = action.payload;
      })
      .addCase(addToCart.fulfilled, (state, action) => {
        state.currentUser.userCart.push(action.payload);
      })
      .addCase(deletePizzaFromCart.fulfilled, (state, action) => {
        state.currentUser.userCart = state.currentUser.userCart.filter(
          item => item.id !== action.payload
        );
      })
      .addCase(updatePizzaDetails.fulfilled, (state, action) => {
        if (action.payload) {
          const updatedPizza = action.payload;
          const index = state.currentUser.userCart.findIndex(
            pizza => pizza.id === updatedPizza.id
          );
          if (index !== -1) {
            state.currentUser.userCart[index] = updatedPizza;
          }
        }
      })
      .addCase(clearUserCart.fulfilled, state => {
        state.currentUser.userCart = [];
      })
      .addCase(setUsers.fulfilled, (state, action) => {
        state.users = action.payload.map(user => {
          if (user.token === state.currentUser.token) {
            return state.currentUser;
          }

          return user;
        });
      })
      .addCase(setUser.pending, state => {
        state.status = "pending";
      })
      .addCase(setUser.fulfilled, (state, action) => {
        state.status = "fulfilled";

        state.currentUser = {
          name: action.payload.name,
          email: action.payload.email,
          id: action.payload.id,
          token: action.payload.token,
          surname: action.payload.surname,
          phoneNumber: action.payload.phoneNumber,
          userCart: action.payload.userCart,
        };
      })
      .addCase(resetUser.fulfilled, state => {
        state.currentUser = {
          name: null,
          email: null,
          id: null,
          token: null,
          surname: null,
          phoneNumber: null,
          userCart: [],
        };
      })
      .addCase(addUser.fulfilled, (state, action) => {
        state.users.push(action.payload);
      });
  },
});

export default userSlice.reducer;
